#include <fuse.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <dirent.h>
#include <errno.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#include "funcionesFat32.h"
#include "operFuse.h"




 /** Aclaracion: FS es el FileSystem cargada en PFS.c que se declara como global y se usa/modifica aqui  **/
extern t_fs *FS;

extern sem_t semaforo;

int32_t fuse_readdir(char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi){

	t_elemento *elemento;
	TNodoLista *nodo;
	Tentrada *contenidoDir;
	if (strcmp(path, "/") == 0) {
		filler(buf, ".", NULL, 0);
		filler(buf, "..", NULL, 0);
	}

	contenidoDir = listarDir(FS,path);
	if(contenidoDir->Nodo != NULL){
		nodo = contenidoDir->Nodo;
		elemento = (t_elemento*)nodo->dato;
		filler(buf, elemento->nombre, NULL, 0);
		while(nodo->Next != NULL){

			nodo = (TNodoLista*)nodo->Next;
			elemento = (t_elemento*)nodo->dato;
			filler(buf, elemento->nombre, NULL, 0);
		}
	}

	return 0;
}

int32_t fuse_open(char *path, struct fuse_file_info *fi) {
	Tentrada* dirElem = NULL;
	t_elemento *elemento = obtener_elem(FS, path, &dirElem);
	int32_t cluster=0,cantClusters=0,cont=0,flag=1;
	extern t_log *log;
	extern int32_t logActivado;
	char cadena[200];
	memset(cadena,'\0',200);

	if (elemento == NULL){
		return -ENOENT;
	}

	if (logActivado){
		cantClusters= elemento->tamanio/TAM_CLUSTER;

		if(!((elemento->tamanio%TAM_CLUSTER)==0))
			cantClusters++;

		cluster = elemento->primerCluster;
		sprintf(cadena,"%d, ",cluster);
		cont++;

		while (!ESFAT_EOC(FS->tablaFAT[cluster])){
			cluster = FS->tablaFAT[cluster];
			cont++;
			if(cont<=10)
				sprintf(cadena,"%s%d, ",cadena,NUM_CLUSTER(cluster));
			else{
				if(cont>=(cantClusters-10))
					sprintf(cadena,"%s%d, ",cadena, NUM_CLUSTER(cluster));
				else{
					if(flag){
						sprintf(cadena,"%s ... ,",cadena, NUM_CLUSTER(cluster));
						flag=0;
					}
				}
			}
		}

		cadena[strlen(cadena)-2]= '.';

		log_info(log,"PFS","Mensaje info: %s \n",cadena);
	}
	elemento->cantAperturas++;
	return 0;
}

int32_t fuse_truncate (char *path, off_t largo){
	Tentrada* dirElem = NULL;
	t_elemento *arch = (t_elemento*) obtener_elem(FS, path, &dirElem);
	int32_t i=0;
	extern t_pool VectorPool[10];

	sem_wait(&semaforo);

	while(VectorPool[i].uso == 1){
		i++;
	}
	VectorPool[i].uso = 1;

	if (arch == NULL){
		return -ENOENT;
	}
	resize_arch(FS, arch, largo,VectorPool[i].Sock);

	VectorPool[i].uso=0;
	sem_post(&semaforo);
	return 0;
}

int32_t fuse_read( char *path, char *buf, size_t tamanio, off_t offset, struct fuse_file_info *fi){
	size_t tamanio2;
	Tentrada* dirElem = NULL;
	int32_t i=0;
	extern t_pool VectorPool[10];

	t_elemento *arch = (t_elemento*) obtener_elem(FS, path, &dirElem);
	sem_wait(&semaforo);

	while(VectorPool[i].uso == 1){
		i++;
	}
	VectorPool[i].uso = 1;
	if (arch == NULL){
		return -ENOENT;
	}

	tamanio2 = leer_arch(FS, arch, buf, offset, tamanio,VectorPool[i].Sock);
	VectorPool[i].uso=0;
	sem_post(&semaforo);

	return tamanio2;
}

int32_t fuse_getattr( char *path, struct stat *stbuf) {  //stbuf estructura stat 0 o 1
	t_elemento *elemento;
	int32_t res = 0;
	Tentrada* dirElem = NULL;

	memset(stbuf, '\0', sizeof(struct stat));

	if (strcmp(path, "/") == 0) {
		stbuf->st_mode = S_IFDIR | S_IREAD | S_IRGRP | S_IROTH;
		stbuf->st_size = 4096;
		stbuf->st_nlink = lista_tamanio(FS->rootDirectoryContent); //cantidad de entradas del Directorio
		return res;
	}

	elemento = obtener_elem(FS, path, &dirElem);

	if( elemento == NULL ){
		return -ENOENT;
	}

	if (elemento->tipo == ENTRADA_DIR) {
		stbuf->st_mode = S_IFDIR | S_IREAD | S_IRGRP | S_IROTH;
		stbuf->st_nlink =  lista_tamanio(((t_elemento*)elemento)->contenido) + 1;
		stbuf->st_size = elemento->tamanio;
		return res;
	}

	if( elemento->tipo == ENTRADA_ARCH ){
		stbuf->st_mode = S_IFREG | S_IREAD | S_IRGRP | S_IROTH | S_IWRITE | S_IWGRP | S_IWOTH;
		stbuf->st_nlink = 1;
		stbuf->st_size = elemento->tamanio;
		return res;
	}

	return -ENOENT;
}

int32_t fuse_create(char *path, mode_t mode, struct fuse_file_info *fi){
	int32_t i=0;
	extern t_pool VectorPool[10];


	sem_wait(&semaforo);

	while(VectorPool[i].uso == 1){
		i++;
	}
	VectorPool[i].uso = 1;

	crear_arch(FS, path,VectorPool[i].Sock);
	VectorPool[i].uso=0;
	sem_post(&semaforo);
	return 0;
}

int32_t fuse_release(char *path, struct fuse_file_info *fi) {

	Tentrada* dirElem = NULL;
	t_cache *cacheAux, *punt;
	t_elemento *elemento = obtener_elem(FS, path, &dirElem);
	int32_t i=0;
	extern t_pool VectorPool[10];


	sem_wait(&semaforo);

	while(VectorPool[i].uso == 1){
		i++;
	}
	VectorPool[i].uso = 1;


	if (elemento == NULL){
		return -ENOENT;
	}

	elemento->cantAperturas--;

	if (elemento->cantAperturas == 0){
		cacheAux = elemento->cache;

		while(cacheAux != NULL){

			punt = cacheAux;
			cacheAux = cacheAux->next;
			if (punt->escrito == 1){
				escribir_cluster(FS,punt->numClus,punt->cluster,VectorPool[i].Sock);
			}
			free(punt);

		}


	}

	VectorPool[i].uso=0;
	sem_post(&semaforo);

	return 0;
}


int32_t fuse_unlink(char *path){
	int32_t i=0;
	extern t_pool VectorPool[10];


	sem_wait(&semaforo);

	while(VectorPool[i].uso == 1){
		i++;
	}
	VectorPool[i].uso = 1;


	remover_arch(FS, path, VectorPool[i].Sock);
	VectorPool[i].uso=0;
	sem_post(&semaforo);

	return 0;

}

int32_t fuse_write(char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi) {
	Tentrada* dirElem = NULL;
	t_elemento *arch = (t_elemento*) obtener_elem(FS, path, &dirElem);
	int32_t i=0;
	extern t_pool VectorPool[10];
	int32_t res;
	if (arch == NULL){
		return -ENOENT;
	}
	sem_wait(&semaforo);
	i=0;
	while(VectorPool[i].uso == 1){
		i++;
	}
	VectorPool[i].uso = 1;
	res = escribir_arch(FS, arch, buf, offset, size, VectorPool[i].Sock);
	VectorPool[i].uso=0;
	sem_post(&semaforo);


	return res;

}

int32_t fuse_flush(char * path, struct fuse_file_info *fi){
	Tentrada* dirElem = NULL;
	t_elemento *arch = (t_elemento*) obtener_elem(FS, path, &dirElem);
	int32_t Sock;
	extern t_pool VectorPool[10];
	int32_t i=0;

	sem_wait(&semaforo);
	i=0;
	while(VectorPool[i].uso == 1){
		i++;
	}
	VectorPool[i].uso = 1;

	if (arch != NULL)
		flush(FS, arch, VectorPool[i].Sock);
	flush_cache_global(FS,VectorPool[i].Sock);
	VectorPool[i].uso=0;
	sem_post(&semaforo);

	return 0;

}

